/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use attestation::entities::attest_request::{validate_token_fmt, AttestRequest, Evidence, Measurement, Nonce};
use attestation::entities::challenge_request::ChallengeRequest;
use attestation::entities::token::{PolicyInfo, TokenResponse};
use attestation::entities::token::ear_token::{EarToken, EarAttesterResult, EarSubmodResult, EarTrustworthinessVector};
use attestation::entities::token::eat_token::{EatToken, EatAttesterResult};
use attestation::entities::token::token_trait::{AttesterResult, Token, TokenType};
use serde_json::json;
use validator::Validate;
use std::collections::HashMap;

#[cfg(test)]
mod attest_request_tests {
    use super::*;

    #[test]
    fn test_validate_attest_request_with_valid_data_should_succeed() {
        let attest_request = AttestRequest {
            agent_version: Some("1.0.0".to_string()),
            measurements: vec![
                Measurement {
                    node_id: "test-node".to_string(),
                    nonce: Some("test-nonce".to_string()),
                    nonce_type: Some("verifier".to_string()),
                    token_fmt: Some("ear".to_string()),
                    attester_data: None,
                    evidences: vec![
                        Evidence {
                             attester_type: "test-attester".to_string(),
                             evidence: json!("test-evidence"),
                             policy_ids: Some(vec!["policy1".to_string()]),
                         }
                    ],
                }
            ],
        };

        assert!(attest_request.validate().is_ok());
    }

    #[test]
    fn test_validate_attest_request_should_fail_when_agent_version_exceeds_50_chars() {
        let attest_request = AttestRequest {
            agent_version: Some("a".repeat(51)),
            measurements: vec![
                Measurement {
                    node_id: "test-node".to_string(),
                    nonce: Some("test-nonce".to_string()),
                    nonce_type: Some("verifier".to_string()),
                    token_fmt: Some("ear".to_string()),
                    attester_data: None,
                    evidences: vec![
                         Evidence {
                             attester_type: "test-attester".to_string(),
                             evidence: json!("test-evidence"),
                             policy_ids: Some(vec!["policy1".to_string()]),
                         }
                     ],
                }
            ],
        };

        assert!(attest_request.validate().is_err());
    }

    #[test]
    fn test_validate_attest_request_should_fail_when_measurements_are_empty() {
        let attest_request = AttestRequest {
            agent_version: Some("1.0.0".to_string()),
            measurements: vec![],
        };

        assert!(attest_request.validate().is_err());
    }

    #[test]
    fn test_validate_measurement_with_valid_data_should_succeed() {
        let measurement = Measurement {
            node_id: "test-node".to_string(),
            nonce: Some("test-nonce".to_string()),
            nonce_type: Some("verifier".to_string()),
            token_fmt: Some("ear".to_string()),
            attester_data: None,
            evidences: vec![
                 Evidence {
                     attester_type: "test-attester".to_string(),
                     evidence: json!("test-evidence"),
                     policy_ids: Some(vec!["policy1".to_string()]),
                 }
             ],
        };

        assert!(measurement.validate().is_ok());
    }

    #[test]
    fn test_validate_measurement_should_fail_when_node_id_exceeds_255_chars() {
        let measurement = Measurement {
            node_id: "a".repeat(256),
            nonce: Some("test-nonce".to_string()),
            nonce_type: Some("verifier".to_string()),
            token_fmt: Some("ear".to_string()),
            attester_data: None,
            evidences: vec![
                 Evidence {
                     attester_type: "test-attester".to_string(),
                     evidence: json!("test-evidence"),
                     policy_ids: Some(vec!["policy1".to_string()]),
                 }
             ],
        };

        assert!(measurement.validate().is_err());
    }

    #[test]
    fn test_validate_measurement_should_fail_when_evidences_are_empty() {
        let measurement = Measurement {
            node_id: "test-node".to_string(),
            nonce: Some("test-nonce".to_string()),
            nonce_type: Some("verifier".to_string()),
            token_fmt: Some("ear".to_string()),
            attester_data: None,
            evidences: vec![],
        };

        assert!(measurement.validate().is_err());
    }

    #[test]
    fn test_validate_evidence_with_valid_data_should_succeed() {
        let evidence = Evidence {
            attester_type: "test-attester".to_string(),
            evidence: json!("test-evidence"),
            policy_ids: Some(vec!["policy1".to_string()]),
        };

        assert!(evidence.validate().is_ok());
    }

    #[test]
    fn test_validate_evidence_should_fail_when_attester_type_exceeds_255_chars() {
        let evidence = Evidence {
            attester_type: "a".repeat(256),
            evidence: json!("test-evidence"),
            policy_ids: Some(vec!["policy1".to_string()]),
        };

        assert!(evidence.validate().is_err());
    }

    #[test]
    fn test_validate_evidence_should_fail_when_policy_ids_exceed_10_items() {
        let evidence = Evidence {
            attester_type: "test-attester".to_string(),
            evidence: json!("test-evidence"),
            policy_ids: Some((0..11).map(|i| format!("policy{}", i)).collect()),
        };

        assert!(evidence.validate().is_err());
    }

    #[test]
    fn test_create_default_nonce_should_initialize_with_zero_values() {
        let nonce = Nonce::default();
        assert_eq!(nonce.iat, 0);
        assert_eq!(nonce.value, "");
        assert_eq!(nonce.signature, "");
    }

    #[test]
    fn test_validate_token_format_should_succeed_with_valid_formats() {
        assert!(validate_token_fmt(&"ear".to_string()).is_ok());
        assert!(validate_token_fmt(&"eat".to_string()).is_ok());
    }

    #[test]
    fn test_validate_token_format_should_fail_with_invalid_formats() {
        assert!(validate_token_fmt(&"invalid".to_string()).is_err());
        assert!(validate_token_fmt(&"".to_string()).is_err());
    }
}

#[cfg(test)]
mod challenge_request_tests {
    use super::*;

    #[test]
    fn test_validate_challenge_request_with_valid_data_should_succeed() {
        let challenge_request = ChallengeRequest {
            agent_version: Some("1.0.0".to_string()),
            attester_type: vec!["test-attester".to_string()],
        };

        assert!(challenge_request.validate().is_ok());
    }

    #[test]
    fn test_validate_challenge_request_should_fail_when_agent_version_exceeds_50_chars() {
        let challenge_request = ChallengeRequest {
            agent_version: Some("a".repeat(51)),
            attester_type: vec!["test-attester".to_string()],
        };

        assert!(challenge_request.validate().is_err());
    }

    #[test]
    fn test_validate_challenge_request_should_fail_when_attester_type_is_empty() {
        let challenge_request = ChallengeRequest {
            agent_version: Some("1.0.0".to_string()),
            attester_type: vec![],
        };

        assert!(challenge_request.validate().is_err());
    }

    #[test]
    fn test_validate_challenge_request_should_fail_when_attester_type_element_exceeds_255_chars() {
        let challenge_request = ChallengeRequest {
            agent_version: Some("1.0.0".to_string()),
            attester_type: vec!["a".repeat(256)],
        };

        assert!(challenge_request.validate().is_err());
    }

    #[test]
    fn test_validate_challenge_request_should_fail_when_attester_type_element_is_empty() {
        let challenge_request = ChallengeRequest {
            agent_version: Some("1.0.0".to_string()),
            attester_type: vec!["".to_string()],
        };

        assert!(challenge_request.validate().is_err());
    }

    #[test]
    fn test_validate_challenge_request_should_succeed_when_agent_version_is_none() {
        let challenge_request = ChallengeRequest {
            agent_version: None,
            attester_type: vec!["test-attester".to_string()],
        };

        assert!(challenge_request.validate().is_ok());
    }
}

#[cfg(test)]
mod token_tests {
    use super::*;

    mod policy_info_tests {
        use super::*;

        #[test]
        fn test_serialize_policy_info_should_exclude_none_custom_data() {
            let policy_info = PolicyInfo {
                appraisal_policy_id: "test-policy".to_string(),
                policy_version: 1,
                policy_matched: true,
                policy_content: "test policy content".to_string(),
                custom_data: None,
            };

            let serialized = serde_json::to_string(&policy_info).unwrap();
            assert!(!serialized.contains("custom_data"));
            assert!(!serialized.contains("policy_content")); // policy_content should be skipped
        }

        #[test]
        fn test_serialize_policy_info_should_include_some_custom_data() {
            let policy_info = PolicyInfo {
                appraisal_policy_id: "test-policy".to_string(),
                policy_version: 1,
                policy_matched: true,
                policy_content: "test policy content".to_string(),
                custom_data: Some(json!({"test": "data"})),
            };

            let serialized = serde_json::to_string(&policy_info).unwrap();
            assert!(serialized.contains("custom_data"));
            assert!(serialized.contains("test"));
            assert!(!serialized.contains("policy_content")); // policy_content should be skipped
        }

        #[test]
        fn test_deserialize_policy_info_should_succeed() {
            let json_str = r#"{
                "appraisal_policy_id": "test-policy",
                "policy_version": 1,
                "policy_matched": true,
                "policy_content": "test policy content",
                "custom_data": {"key": "value"}
            }"#;

            let policy_info: PolicyInfo = serde_json::from_str(json_str).unwrap();
            assert_eq!(policy_info.appraisal_policy_id, "test-policy");
            assert_eq!(policy_info.policy_version, 1);
            assert!(policy_info.policy_matched);
            assert!(policy_info.custom_data.is_some());
        }
    }

    mod token_response_tests {
        use super::*;

        #[test]
        fn test_serialize_token_response_should_succeed() {
            let token_response = TokenResponse {
                node_id: "test-node".to_string(),
                token: "test-token".to_string(),
            };

            let serialized = serde_json::to_string(&token_response).unwrap();
            assert!(serialized.contains("node_id"));
            assert!(serialized.contains("token"));
            assert!(serialized.contains("test-node"));
            assert!(serialized.contains("test-token"));
        }

        #[test]
        fn test_deserialize_token_response_should_succeed() {
            let json_str = r#"{
                "node_id": "test-node",
                "token": "test-token"
            }"#;

            let token_response: TokenResponse = serde_json::from_str(json_str).unwrap();
            assert_eq!(token_response.node_id, "test-node");
            assert_eq!(token_response.token, "test-token");
        }

        #[test]
        fn test_clone_token_response_should_succeed() {
            let original = TokenResponse {
                node_id: "test-node".to_string(),
                token: "test-token".to_string(),
            };

            let cloned = original.clone();
            assert_eq!(original.node_id, cloned.node_id);
            assert_eq!(original.token, cloned.token);
        }
    }

    mod ear_token_tests {
        use super::*;

        #[test]
        fn test_create_new_ear_token_should_initialize_with_defaults() {
            let ear_token = EarToken::new();

            assert_eq!(ear_token.ear_verifier_id.developer, "https://gitee.com/openeuler/global-trust-authority");
            assert_eq!(ear_token.ear_verifier_id.version, "v1.0.0");
            assert!(ear_token.matched_policy.is_empty());
            assert!(ear_token.unmatched_policy.is_empty());
            assert!(ear_token.ueid.is_none());
            assert!(ear_token.ear_raw_evidence.is_none());
            assert!(ear_token.submods.is_empty());
        }

        #[test]
        fn test_ear_token_create_evidence_response_with_all_pass_should_return_affirming() {
            let mut ear_token = EarToken::new();
            let verify_results = vec![true, true, true];
            let raw_evidence = Some(json!({"test": "evidence"}));
            let policy_info = vec![PolicyInfo {
                appraisal_policy_id: "test-policy".to_string(),
                policy_version: 1,
                policy_matched: true,
                policy_content: "test policy content".to_string(),
                custom_data: None,
            }];

            let result = ear_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            match result {
                AttesterResult::Ear(ear_result) => {
                    assert_eq!(ear_result.ear_status, "affirming");
                    assert!(ear_result.ear_appraisal_policy_id.is_some());
                    assert_eq!(ear_result.ear_appraisal_policy_id.unwrap(), "test-policy");
                }
                _ => panic!("Expected EAR result"),
            }
        }

        #[test]
        fn test_ear_token_create_evidence_response_with_some_fail_should_return_contraindicated() {
            let mut ear_token = EarToken::new();
            let verify_results = vec![true, false, true];
            let raw_evidence = Some(json!({"test": "evidence"}));
            let policy_info = vec![PolicyInfo {
                appraisal_policy_id: "test-policy".to_string(),
                policy_version: 1,
                policy_matched: false,
                policy_content: "test policy content".to_string(),
                custom_data: None,
            }];

            let result = ear_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            match result {
                AttesterResult::Ear(ear_result) => {
                    assert_eq!(ear_result.ear_status, "contarindicated");
                }
                _ => panic!("Expected EAR result"),
            }
        }

        #[test]
        fn test_ear_token_create_evidence_response_with_empty_policy_should_return_none() {
            let mut ear_token = EarToken::new();
            let verify_results = vec![true];
            let raw_evidence = Some(json!({"test": "evidence"}));
            let policy_info = vec![];

            let result = ear_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            match result {
                AttesterResult::Ear(ear_result) => {
                    assert_eq!(ear_result.ear_status, "none");
                    assert!(ear_result.ear_appraisal_policy_id.is_none());
                }
                _ => panic!("Expected EAR result"),
            }
        }

        #[test]
        fn test_ear_token_create_attestation_response_should_succeed() {
            let ear_token = EarToken::new();
            let mut evidence_responses = HashMap::new();
            evidence_responses.insert("test_attester".to_string(), AttesterResult::Ear(EarAttesterResult {
                ear_status: "affirming".to_string(),
                ear_trustworthiness_vector: None,
                ear_appraisal_policy_id: Some("test-policy".to_string()),
                ear_gta_annotated_evidence: None,
            }));

            let measurement = Measurement {
                node_id: "test-node".to_string(),
                nonce: Some("test-nonce".to_string()),
                nonce_type: Some("verifier".to_string()),
                token_fmt: Some("ear".to_string()),
                attester_data: None,
                evidences: vec![],
            };

            let result = ear_token.create_attestation_response(&evidence_responses, "verifier", &Some("test-nonce".to_string()), &measurement);

            match result {
                TokenType::Ear(ear_token) => {
                    assert_eq!(ear_token.ueid, Some("test-node".to_string()));
                    assert!(!ear_token.submods.is_empty());
                }
                _ => panic!("Expected EAR token type"),
            }
        }

        #[test]
        fn test_ear_trustworthiness_vector_creation_should_succeed() {
            let vector = EarTrustworthinessVector {
                executables: Some(1),
                hardware: Some(2),
            };

            assert_eq!(vector.executables, Some(1));
            assert_eq!(vector.hardware, Some(2));
        }

        #[test]
        fn test_ear_submod_result_creation_should_succeed() {
            let mut ear_attester_results = HashMap::new();
            ear_attester_results.insert("test_attester".to_string(), EarAttesterResult {
                ear_status: "affirming".to_string(),
                ear_trustworthiness_vector: None,
                ear_appraisal_policy_id: Some("test-policy".to_string()),
                ear_gta_annotated_evidence: None,
            });

            let submod = EarSubmodResult {
                ear_attester_results,
            };

            assert!(!submod.ear_attester_results.is_empty());
            assert!(submod.ear_attester_results.contains_key("test_attester"));
        }
    }

    mod eat_token_tests {
        use super::*;

        #[test]
        fn test_create_new_eat_token_should_initialize_with_defaults() {
            let eat_token = EatToken::new();

            assert!(eat_token.nonce_type.is_empty());
            assert!(eat_token.intuse.is_none());
            assert!(eat_token.eat_nonce.is_none());
            assert!(eat_token.attester_data.is_none());
            assert!(eat_token.ueid.is_none());
            assert!(eat_token.matched_policy.is_empty());
            assert!(eat_token.unmatched_policy.is_empty());
            assert!(eat_token.results.is_empty());
        }

        #[test]
        fn test_eat_token_create_evidence_response_with_all_pass_should_return_pass() {
            let mut eat_token = EatToken::new();
            let verify_results = vec![true, true, true];
            let raw_evidence = Some(json!({
                "annotated_evidence": {"test": "evidence"}
            }));
            let policy_info = vec![PolicyInfo {
                appraisal_policy_id: "test-policy".to_string(),
                policy_version: 1,
                policy_matched: true,
                policy_content: "test policy content".to_string(),
                custom_data: None,
            }];

            let result = eat_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            match result {
                AttesterResult::Eat(eat_result) => {
                    assert_eq!(eat_result.attestation_status, "pass");
                    assert!(!eat_result.policy_info.is_empty());
                    assert!(eat_result.raw_evidence.is_some());
                }
                _ => panic!("Expected EAT result"),
            }
        }

        #[test]
        fn test_eat_token_create_evidence_response_with_some_fail_should_return_fail() {
            let mut eat_token = EatToken::new();
            let verify_results = vec![true, false, true];
            let raw_evidence = Some(json!({"test": "evidence"}));
            let policy_info = vec![PolicyInfo {
                appraisal_policy_id: "test-policy".to_string(),
                policy_version: 1,
                policy_matched: false,
                policy_content: "test policy content".to_string(),
                custom_data: None,
            }];

            let result = eat_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            match result {
                AttesterResult::Eat(eat_result) => {
                    assert_eq!(eat_result.attestation_status, "fail");
                }
                _ => panic!("Expected EAT result"),
            }
        }

        #[test]
        fn test_eat_token_create_evidence_response_with_empty_policy_should_return_unknown() {
            let mut eat_token = EatToken::new();
            let verify_results = vec![true];
            let raw_evidence = Some(json!({"test": "evidence"}));
            let policy_info = vec![];

            let result = eat_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            match result {
                AttesterResult::Eat(eat_result) => {
                    assert_eq!(eat_result.attestation_status, "unknown");
                    assert!(eat_result.policy_info.is_empty());
                }
                _ => panic!("Expected EAT result"),
            }
        }

        #[test]
        fn test_eat_token_create_evidence_response_should_update_policy_lists() {
            let mut eat_token = EatToken::new();
            let verify_results = vec![true];
            let raw_evidence = None;
            let policy_info = vec![
                PolicyInfo {
                    appraisal_policy_id: "matched-policy".to_string(),
                    policy_version: 1,
                    policy_matched: true,
                    policy_content: "matched policy content".to_string(),
                    custom_data: None,
                },
                PolicyInfo {
                    appraisal_policy_id: "unmatched-policy".to_string(),
                    policy_version: 1,
                    policy_matched: false,
                    policy_content: "unmatched policy content".to_string(),
                    custom_data: None,
                },
            ];

            eat_token.create_evidence_response(verify_results, raw_evidence, policy_info);

            // Check that matched_policy contains PolicyMatchedInfo with correct id
            assert!(eat_token.matched_policy.iter().any(|p| p.id == "matched-policy"));
            assert!(eat_token.unmatched_policy.iter().any(|p| p.id == "unmatched-policy"));
        }

        #[test]
        fn test_eat_token_create_attestation_response_should_succeed() {
            let eat_token = EatToken::new();
            let mut evidence_responses = HashMap::new();
            evidence_responses.insert("test_attester".to_string(), AttesterResult::Eat(EatAttesterResult {
                attestation_status: "pass".to_string(),
                policy_info: vec![],
                raw_evidence: None,
            }));

            let measurement = Measurement {
                node_id: "test-node".to_string(),
                nonce: Some("test-nonce".to_string()),
                nonce_type: Some("verifier".to_string()),
                token_fmt: Some("eat".to_string()),
                attester_data: Some(json!({"key": "value"})),
                evidences: vec![],
            };

            let result = eat_token.create_attestation_response(&evidence_responses, "verifier", &Some("test-nonce".to_string()), &measurement);

            match result {
                TokenType::Eat(eat_token) => {
                    assert_eq!(eat_token.nonce_type, "verifier");
                    assert_eq!(eat_token.ueid, Some("test-node".to_string()));
                    assert!(eat_token.attester_data.is_some());
                    assert!(!eat_token.results.is_empty());
                }
                _ => panic!("Expected EAT token type"),
            }
        }

        #[test]
        fn test_eat_attester_result_creation_should_succeed() {
            let result = EatAttesterResult {
                attestation_status: "pass".to_string(),
                policy_info: vec![PolicyInfo {
                    appraisal_policy_id: "test-policy".to_string(),
                    policy_version: 1,
                    policy_matched: true,
                    policy_content: "test policy content".to_string(),
                    custom_data: None,
                }],
                raw_evidence: Some(json!({"test": "evidence"})),
            };

            assert_eq!(result.attestation_status, "pass");
            assert!(!result.policy_info.is_empty());
            assert!(result.raw_evidence.is_some());
        }

        #[test]
        fn test_eat_token_serialize_should_exclude_empty_policy_info() {
            let result = EatAttesterResult {
                attestation_status: "pass".to_string(),
                policy_info: vec![],
                raw_evidence: None,
            };

            let serialized = serde_json::to_string(&result).unwrap();
            assert!(!serialized.contains("policy_info"));
        }

        #[test]
        fn test_eat_token_serialize_should_include_non_empty_policy_info() {
            let result = EatAttesterResult {
                attestation_status: "pass".to_string(),
                policy_info: vec![PolicyInfo {
                    appraisal_policy_id: "test-policy".to_string(),
                    policy_version: 1,
                    policy_matched: true,
                    policy_content: "test policy content".to_string(),
                    custom_data: None,
                }],
                raw_evidence: None,
            };

            let serialized = serde_json::to_string(&result).unwrap();
            assert!(serialized.contains("policy_info"));
        }
    }

    mod attester_result_tests {
        use super::*;

        #[test]
        fn test_attester_result_eat_variant_should_succeed() {
            let eat_result = EatAttesterResult {
                attestation_status: "pass".to_string(),
                policy_info: vec![],
                raw_evidence: None,
            };

            let attester_result = AttesterResult::Eat(eat_result);

            match attester_result {
                AttesterResult::Eat(result) => {
                    assert_eq!(result.attestation_status, "pass");
                }
                _ => panic!("Expected EAT variant"),
            }
        }

        #[test]
        fn test_attester_result_ear_variant_should_succeed() {
            let ear_result = EarAttesterResult {
                ear_status: "affirming".to_string(),
                ear_trustworthiness_vector: None,
                ear_appraisal_policy_id: Some("test-policy".to_string()),
                ear_gta_annotated_evidence: None,
            };

            let attester_result = AttesterResult::Ear(ear_result);

            match attester_result {
                AttesterResult::Ear(result) => {
                    assert_eq!(result.ear_status, "affirming");
                }
                _ => panic!("Expected EAR variant"),
            }
        }
    }

    mod token_type_tests {
        use super::*;

        #[test]
        fn test_token_type_eat_variant_should_succeed() {
            let eat_token = EatToken::new();
            let token_type = TokenType::Eat(eat_token);

            match token_type {
                TokenType::Eat(token) => {
                    assert!(token.nonce_type.is_empty());
                }
                _ => panic!("Expected EAT variant"),
            }
        }

        #[test]
        fn test_token_type_ear_variant_should_succeed() {
            let ear_token = EarToken::new();
            let token_type = TokenType::Ear(ear_token);

            match token_type {
                TokenType::Ear(token) => {
                    assert_eq!(token.ear_verifier_id.developer, "https://gitee.com/openeuler/global-trust-authority");
                }
                _ => panic!("Expected EAR variant"),
            }
        }
    }
}
